/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.web.common.utility;

import com.inspur.edp.web.common.logger.WebLogger;
import org.apache.commons.lang3.SystemUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;

/**
 * 调用其他命令执行
 * 通过子线程触发，避免阻塞主线程
 *
 * @author guozhiqi
 */
public class CommandExecuteInterceptor extends Thread {

    /**
     * 子线程的实例变量 在多线程中要避免对实例变量的访问与修改
     */
    private final InputStream inputStream;
    /**
     * 外部传入的共享变量
     * 此变量StringBuilder为非线程安全，在多线程同时执行时，会导致数据不完整
     * 因此调整为StringBuffer. 由于StringBuilder与StringBuffer 方法兼容，因此可以直接进行替换
     */
    private final StringBuffer errorSB;
    /**
     * 是否需要重新执行安装操作
     */
    private boolean needReInstall = false;

    private boolean isErrorStream = false;

    public CommandExecuteInterceptor(InputStream is, StringBuffer errorSB, boolean isErrorStream) {
        this.inputStream = is;
        // 如果传递的StringBuffer实例为空 那么创建对应的实例
        if (errorSB == null) {
            this.errorSB = new StringBuffer();
        } else {
            this.errorSB = errorSB;
        }
        this.isErrorStream = isErrorStream;
    }

    @Override
    public void run() {
        BufferedReader br = null;
        try {
            Charset charset = Charset.defaultCharset();
            if (SystemUtils.IS_OS_WINDOWS) {
                charset = Charset.forName("GBK");
            }
            InputStreamReader inputStreamReader = new InputStreamReader(this.inputStream, charset);
            //读取进程输入流中的内容
            String line = null;
            br = new BufferedReader(inputStreamReader);
            boolean needAddErrorMessage = true;
            while ((line = br.readLine()) != null) {
                WebLogger.Instance.info(line, this.getClass().getName());
                //判断是否存在错误描述
                if (CommandLineUtility.checkHasError(line) || checkCommandIsNotExists(line)) {
                    // 针对特定的输出标识  继续查找下一个输出信息
                    if (line.contains("npm ERR! code ETARGET")) {
                        addErrorMessage(errorSB, line, needAddErrorMessage);
                    } else if (line.contains("npm ERR! notarget No matching version found for")) {
                        // 针对特定异常进行提取
                        if (!needAddErrorMessage) {
                            continue;
                        }
                        String notMatchingVersionTest = "npm ERR! notarget No matching version found for";
                        String notMatchedPackageVersion = line.substring(line.indexOf(notMatchingVersionTest) + notMatchingVersionTest.length()).trim();
                        errorSB.delete(0, errorSB.length());
                        addErrorMessage(errorSB, "npm ERR! ", needAddErrorMessage);
                        addErrorMessage(errorSB, notMatchedPackageVersion, needAddErrorMessage);
                        addErrorMessage(errorSB, "   未找到对应版本，请修正后重新执行安装！", needAddErrorMessage);

                        needAddErrorMessage = false;
                    } else if (line.contains("npm ERR! 404") && line.contains("is not in the npm registry")) {
                        // 如果当前仓库不存在指定npm包
                        if (!needAddErrorMessage) {
                            continue;
                        }
                        String notMatchingVersionTest = "npm ERR! 404";
                        String lastIndexText = "is not in the npm registry";
                        String notExistsPackage = line.substring(line.indexOf(notMatchingVersionTest) + notMatchingVersionTest.length(), line.indexOf(lastIndexText));
                        errorSB.delete(0, errorSB.length());
                        addErrorMessage(errorSB, "npm ERR! ", needAddErrorMessage);
                        addErrorMessage(errorSB, "当前仓库" + notExistsPackage + "包不存在，请切换至其他仓库或修正包版本，然后重新进行安装！", needAddErrorMessage);
                        needAddErrorMessage = false;
                    } else if (line.contains("npm ERR! errno ERR_SOCKET_TIMEOUT")) {
                        if (!needAddErrorMessage) {
                            continue;
                        }
                        errorSB.delete(0, errorSB.length());
                        addErrorMessage(errorSB, "npm ERR! ", needAddErrorMessage);
                        addErrorMessage(errorSB, "连接服务器超时，请重新安装或切换至其他仓库，然后重新进行安装！", needAddErrorMessage);
                        needAddErrorMessage = false;

                        needReInstall = true;
                    } else if (line.contains("Error: EPERM: operation not permitted, unlink")) {
                        if (!needAddErrorMessage) {
                            continue;
                        }
                        errorSB.delete(0, errorSB.length());
                        addErrorMessage(errorSB, "npm ERR! ", needAddErrorMessage);
                        addErrorMessage(errorSB, "权限不足，请以管理员权限运行以进行Npm在线安装！", needAddErrorMessage);
                        needAddErrorMessage = false;

                        needReInstall = true;
                    } else if (line.contains("npm ERR! Maximum call stack size exceeded")) {
                        if (!needAddErrorMessage) {
                            continue;
                        }
                        errorSB.delete(0, errorSB.length());
                        addErrorMessage(errorSB, "npm ERR! ", needAddErrorMessage);
                        addErrorMessage(errorSB, "Maximum call stack size exceeded", needAddErrorMessage);
                        needAddErrorMessage = false;

                        needReInstall = true;
                    } else {
                        // 表示命令找不到的提示信息  进行截取
                        if (line.contains("rollup build failed")) {
                            addErrorMessage(errorSB, line, needAddErrorMessage);
                        } else {
                            addErrorMessage(errorSB, line, needAddErrorMessage);
                        }
                    }
                }
            }
        } catch (IOException e) {
            WebLogger.Instance.error(e);
        } finally {
            try {
                this.inputStream.close();
                br.close();
            } catch (IOException ex) {
                WebLogger.Instance.error(ex);
            }
        }
    }

    /**
     * 检测命令是否存在的方法
     *
     * @param line
     * @return
     */
    private boolean checkCommandIsNotExists(String line) {
        if (StringUtility.isNullOrEmpty(line)) {
            return false;
        }
        return line.contains("不是内部或外部命令，也不是可运行的程序") || line.contains("command not found") || line.contains("exec: node: not found")
                || line.contains("‘node’: No such file or directory") || line.contains("No such file or directory");
    }

    private void addErrorMessage(StringBuffer sb, String errorMessage, boolean needAddErrorMessage) {
        if (!needAddErrorMessage) {
            return;
        }
        sb.append(errorMessage);
    }
}
